import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;

import java.util.ArrayList;
import java.util.List;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
import static org.mybatis.generator.internal.util.messages.Messages.getString;

public class ServiceAndControllerGeneratorPlugin extends PluginAdapter {

    // 项目目录，一般为 src/main/java
    private String targetProject;

    // service包名，如：com.thinkj2ee.cms.service.service
    private String servicePackage;

    private String managerPackage;

    // service实现类包名，如：com.thinkj2ee.cms.service.service.impl
    private String serviceImplPackage;
    private String managerImplPackage;
    // Controlle类包名，如：com.thinkj2ee.cms.service.controller
    private String controllerPackage;
    // service接口名前缀
    private String servicePreffix;
    private String managerPreffix;

    // service接口名后缀
    private String serviceSuffix;
    private String managerSuffix;

    //do包
    private String doPackage;

    // service接口的父接口
    //private String superServiceInterface;

    // service实现类的父类
    //private String superServiceImpl;
    // controller类的父类
    //private String superController;

    // dao接口基类
    private String superDaoInterface;

    // Example类的包名
    private String examplePacket;

    private String recordType;

    private String modelName;

    private FullyQualifiedJavaType model;
    private FullyQualifiedJavaType modelExample;

    private String serviceName;
    private String managerName;
    private String serviceImplName;
    private String managerImplName;
    private String controllerName;

    @Override
    public boolean validate(List<String> warnings) {
        boolean valid = true;

       /* if (!stringHasValue(properties
                .getProperty("targetProject"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "targetProject")); //$NON-NLS-1$
            valid = false;
        }
        if (!stringHasValue(properties.getProperty("servicePackage"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "servicePackage")); //$NON-NLS-1$
            valid = false;
        }
        if (!stringHasValue(properties.getProperty("serviceImplPackage"))) { //$NON-NLS-1$
            warnings.add(getString("ValidationError.18", //$NON-NLS-1$
                    "MapperConfigPlugin", //$NON-NLS-1$
                    "serviceImplPackage")); //$NON-NLS-1$
            valid = false;
        }
*/
        targetProject = properties.getProperty("targetProject");
        servicePackage = properties.getProperty("servicePackage")+".service";
        serviceImplPackage = servicePackage+".impl";
        servicePreffix = properties.getProperty("servicePreffix");
        servicePreffix = stringHasValue(servicePreffix) ? servicePreffix : "";
        serviceSuffix = properties.getProperty("serviceSuffix");
        serviceSuffix = stringHasValue(serviceSuffix) ? serviceSuffix : "";

        managerPackage = properties.getProperty("servicePackage")+".manager";
        managerImplPackage = managerPackage+".impl";
        managerPreffix = properties.getProperty("managerPreffix");

        managerPreffix = stringHasValue(managerPreffix) ? managerPreffix : "";
        managerSuffix = properties.getProperty("managerSuffix");
        managerSuffix = stringHasValue(managerSuffix) ? managerSuffix : "";
        //superServiceInterface = properties.getProperty("superServiceInterface");
        //superServiceImpl = properties.getProperty("superServiceImpl");
        superDaoInterface = properties.getProperty("superDaoInterface");
        controllerPackage = properties.getProperty("servicePackage")+".controller";
        doPackage = properties.getProperty("doPackage");
        //superController = properties.getProperty("superController");

        return valid;
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        recordType = introspectedTable.getBaseRecordType();
        modelName = recordType.substring(recordType.lastIndexOf(".") + 1);
        model = new FullyQualifiedJavaType(recordType);
        modelExample = new FullyQualifiedJavaType(recordType+"Example");
        serviceName = servicePackage + "." + servicePreffix + modelName + serviceSuffix;
        serviceImplName = serviceImplPackage + "." + modelName + serviceSuffix + "Impl";
        managerName = managerPackage + "." + managerPreffix + modelName + managerSuffix;
        managerImplName = managerImplPackage + "." + modelName + managerSuffix + "Impl";
        examplePacket = recordType.substring(0, recordType.lastIndexOf("."));
        controllerName = controllerPackage.concat(".").concat(modelName).concat("Controller");
        doPackage = doPackage.concat(".").concat(modelName);
        List<GeneratedJavaFile> answer = new ArrayList<>();

        GeneratedJavaFile gjf = generateServiceInterface(introspectedTable);
        GeneratedJavaFile gjf2 = generateServiceImpl(introspectedTable);
        GeneratedJavaFile gjf3 = generateController(introspectedTable);
        GeneratedJavaFile gjf4 = generateManagerInterface(introspectedTable);
        GeneratedJavaFile gjf5 = generateManagerImpl(introspectedTable);
        answer.add(gjf);
        answer.add(gjf2);
        answer.add(gjf3);
        answer.add(gjf4);
        answer.add(gjf5);
        return answer;
    }

    // 生成service接口
    private GeneratedJavaFile generateServiceInterface(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        Interface serviceInterface = new Interface(service);
        serviceInterface.setVisibility(JavaVisibility.PUBLIC);
        serviceInterface.addImportedType(new FullyQualifiedJavaType("com.github.pagehelper.*"));
        serviceInterface.addImportedType(new FullyQualifiedJavaType("com.alibaba.fastjson.JSONObject"));
        //引入实体类
        serviceInterface.addImportedType(model);
        // 添加父接口
//        if (stringHasValue(superServiceInterface)) {
//            String superServiceInterfaceName = superServiceInterface.substring(superServiceInterface.lastIndexOf(".") + 1);
//            serviceInterface.addImportedType(new FullyQualifiedJavaType(superServiceInterface));
//            serviceInterface.addImportedType(new FullyQualifiedJavaType(recordType));
//            serviceInterface.addSuperInterface(new FullyQualifiedJavaType(superServiceInterfaceName + "<" + modelName + ">"));
//        }
        Method method = new Method("get");
        serviceInterface.addMethod(method);
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType("PageInfo<"+modelName+">");
        //返回值
        method.setReturnType(methodReturnType);
        method.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));
        //method.addException(new FullyQualifiedJavaType("Exception"));

        GeneratedJavaFile gjf = new GeneratedJavaFile(serviceInterface, targetProject, context.getJavaFormatter());
        return gjf;
    }

    // 生成serviceImpl实现类
    private GeneratedJavaFile generateServiceImpl(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        FullyQualifiedJavaType serviceImpl = new FullyQualifiedJavaType(serviceImplName);
        TopLevelClass clazz = new TopLevelClass(serviceImpl);
        //描述类的作用域修饰符
        clazz.setVisibility(JavaVisibility.PUBLIC);
        //描述类 引入的类
        clazz.addImportedType(service);
        //引入实体类
        clazz.addImportedType(model);
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));
        clazz.addImportedType(new FullyQualifiedJavaType("com.alibaba.fastjson.JSONObject"));
        //描述类 的实现接口类
        clazz.addSuperInterface(service);
//        if (stringHasValue(superServiceImpl)) {
//            String superServiceImplName = superServiceImpl.substring(superServiceImpl.lastIndexOf(".") + 1);
//            clazz.addImportedType(superServiceImpl);
//            clazz.addImportedType(recordType);
//            clazz.setSuperClass(superServiceImplName + "<" + modelName + ">");
//        }
        clazz.addImportedType(new FullyQualifiedJavaType("com.github.pagehelper.*"));
        clazz.addAnnotation("@Service");

        //String daoFieldType = introspectedTable.getMyBatis3JavaMapperType();
        //String daoFieldName = firstCharToLowCase(daoFieldType.substring(daoFieldType.lastIndexOf(".") + 1));
        //描述类的成员属性
        String managerFieldName = firstCharToLowCase(managerName.substring(managerName.lastIndexOf(".") + 1));
        Field daoField = new Field(managerFieldName, new FullyQualifiedJavaType(managerName));
        //引入Service
        FullyQualifiedJavaType manager = new FullyQualifiedJavaType(managerName);
        clazz.addImportedType(manager);
        //clazz.addImportedType(new FullyQualifiedJavaType(daoFieldType));
        clazz.addImportedType(new FullyQualifiedJavaType("javax.annotation.Resource"));
        //描述成员属性 的注解
        daoField.addAnnotation("@Resource");
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        clazz.addField(daoField);

        //描述 方法名
        Method method = new Method("get");
        //方法注解
        method.addAnnotation("@Override");
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType("PageInfo"+ "<" + modelName + ">");
        method.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));
        //返回值
        method.setReturnType(methodReturnType);
        //方法体，逻辑代码
        method.addBodyLine("if (jsonObject.containsKey(\"pageNum\") && jsonObject.containsKey(\"pageSize\")) {PageHelper.startPage(jsonObject.getInteger(\"pageNum\"), jsonObject.getInteger(\"pageSize\"));}");
        method.addBodyLine("PageInfo<"+modelName+"> pageInfo =  new PageInfo<>("+managerFieldName+".selectByExample(jsonObject));");
        method.addBodyLine("return pageInfo;");
        //修饰符
        method.setVisibility(JavaVisibility.PUBLIC);
        clazz.addMethod(method);

//        Method method1 = new Method("getExample");
//        method1.addAnnotation("@Override");
//        FullyQualifiedJavaType methodReturnType1 = new FullyQualifiedJavaType("Object");
//        clazz.addImportedType(new FullyQualifiedJavaType(examplePacket.concat(".").concat(modelName).concat("Example")));
//        method1.setReturnType(methodReturnType1);
//        method1.addBodyLine("return new " + modelName + "Example();");
//        method1.setVisibility(JavaVisibility.PUBLIC);
//        clazz.addMethod(method1);

        GeneratedJavaFile gjf2 = new GeneratedJavaFile(clazz, targetProject, context.getJavaFormatter());
        return gjf2;
    }


    // 生成manager接口
    private GeneratedJavaFile generateManagerInterface(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType manager = new FullyQualifiedJavaType(managerName);
        Interface managerInterface = new Interface(manager);
        managerInterface.setVisibility(JavaVisibility.PUBLIC);
        managerInterface.addImportedType(new FullyQualifiedJavaType("java.util.*"));
        managerInterface.addImportedType(new FullyQualifiedJavaType("com.alibaba.fastjson.JSONObject"));
        //引入实体类
        managerInterface.addImportedType(model);
        Method method = new Method("selectByExample");

        managerInterface.addMethod(method);
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType("List<"+modelName+">");
        //返回值
        method.setReturnType(methodReturnType);
        method.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));

        //countByExample
        Method countByExample = new Method("countByExample");
        managerInterface.addMethod(countByExample);
        FullyQualifiedJavaType countByExampleReturnType = new FullyQualifiedJavaType("long");
        //返回值
        countByExample.setReturnType(countByExampleReturnType);
        countByExample.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));

        //deleteByExample
        Method deleteByExample = new Method("deleteByExample");
        managerInterface.addMethod(deleteByExample);
        FullyQualifiedJavaType deleteByExampleReturnType = new FullyQualifiedJavaType("int");
        //返回值
        deleteByExample.setReturnType(deleteByExampleReturnType);
        deleteByExample.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));

        //deleteByPrimaryKey
        Method deleteByPrimaryKey = new Method("deleteByPrimaryKey");
        managerInterface.addMethod(deleteByPrimaryKey);
        FullyQualifiedJavaType deleteByPrimaryKeyReturnType = new FullyQualifiedJavaType("int");
        //返回值
        deleteByPrimaryKey.setReturnType(deleteByPrimaryKeyReturnType);
        deleteByPrimaryKey.addParameter(new Parameter(new FullyQualifiedJavaType("Long"),"id"));

        //insert
        Method insert = new Method("insert");
        managerInterface.addMethod(insert);
        FullyQualifiedJavaType insertReturnType = new FullyQualifiedJavaType("int");
        //返回值
        insert.setReturnType(insertReturnType);
        insert.addParameter(new Parameter(new FullyQualifiedJavaType(modelName),"entity"));

        //insertSelective
        Method insertSelective = new Method("insertSelective");
        managerInterface.addMethod(insertSelective);
        FullyQualifiedJavaType insertSelectiveReturnType = new FullyQualifiedJavaType("int");
        //返回值
        insertSelective.setReturnType(insertSelectiveReturnType);
        insertSelective.addParameter(new Parameter(new FullyQualifiedJavaType(modelName),"entity"));

        //selectByPrimaryKey
        Method selectByPrimaryKey = new Method("selectByPrimaryKey");
        managerInterface.addMethod(selectByPrimaryKey);
        FullyQualifiedJavaType selectByPrimaryKeyReturnType = new FullyQualifiedJavaType(modelName);
        //返回值
        selectByPrimaryKey.setReturnType(selectByPrimaryKeyReturnType);
        selectByPrimaryKey.addParameter(new Parameter(new FullyQualifiedJavaType("Long"),"id"));

        //updateByExampleSelective
        Method updateByExampleSelective = new Method("updateByExampleSelective");
        managerInterface.addMethod(updateByExampleSelective);
        FullyQualifiedJavaType updateByExampleSelectiveReturnType = new FullyQualifiedJavaType("int");
        //返回值
        updateByExampleSelective.setReturnType(updateByExampleSelectiveReturnType);
        updateByExampleSelective.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));

        //updateByExample
        Method updateByExample = new Method("updateByExample");
        managerInterface.addMethod(updateByExample);
        FullyQualifiedJavaType updateByExampleReturnType = new FullyQualifiedJavaType("int");
        //返回值
        updateByExample.setReturnType(updateByExampleReturnType);
        updateByExample.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));

        //updateByPrimaryKeySelective
        Method updateByPrimaryKeySelective = new Method("updateByPrimaryKeySelective");
        managerInterface.addMethod(updateByPrimaryKeySelective);
        FullyQualifiedJavaType updateByPrimaryKeySelectiveReturnType = new FullyQualifiedJavaType("int");
        //返回值
        updateByPrimaryKeySelective.setReturnType(updateByPrimaryKeySelectiveReturnType);
        updateByPrimaryKeySelective.addParameter(new Parameter(new FullyQualifiedJavaType(modelName),"entity"));

        //updateByPrimaryKey
        Method updateByPrimaryKey = new Method("updateByPrimaryKey");
        managerInterface.addMethod(updateByPrimaryKey);
        FullyQualifiedJavaType updateByPrimaryKeyReturnType = new FullyQualifiedJavaType("int");
        //返回值
        updateByPrimaryKey.setReturnType(updateByPrimaryKeyReturnType);
        updateByPrimaryKey.addParameter(new Parameter(new FullyQualifiedJavaType(modelName),"entity"));

        GeneratedJavaFile gjf = new GeneratedJavaFile(managerInterface, targetProject, context.getJavaFormatter());
        return gjf;
    }

    // 生成managerImpl实现类
    private GeneratedJavaFile generateManagerImpl(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType manager = new FullyQualifiedJavaType(managerName);
        FullyQualifiedJavaType managerImpl = new FullyQualifiedJavaType(managerImplName);
        TopLevelClass clazz = new TopLevelClass(managerImpl);
        //描述类的作用域修饰符
        clazz.setVisibility(JavaVisibility.PUBLIC);
        //描述类 引入的类
        clazz.addImportedType(manager);
        //引入实体类
        clazz.addImportedType(model);
        clazz.addImportedType(modelExample);
        clazz.addImportedType(new FullyQualifiedJavaType("java.util.*"));
        clazz.addImportedType(new FullyQualifiedJavaType("com.alibaba.fastjson.JSONObject"));
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Component"));
        //描述类 的实现接口类
        clazz.addSuperInterface(manager);
        //clazz.addImportedType(new FullyQualifiedJavaType("com.github.pagehelper.*"));
        clazz.addAnnotation("@Component");

        String daoFieldType = introspectedTable.getMyBatis3JavaMapperType();
        String daoFieldName = firstCharToLowCase(daoFieldType.substring(daoFieldType.lastIndexOf(".") + 1));
        //描述类的成员属性
        Field daoField = new Field(daoFieldName, new FullyQualifiedJavaType(daoFieldType));
        clazz.addImportedType(new FullyQualifiedJavaType(daoFieldType));
        clazz.addImportedType(new FullyQualifiedJavaType("javax.annotation.Resource"));
        //描述成员属性 的注解
        daoField.addAnnotation("@Resource");
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        clazz.addField(daoField);

        //描述 方法名
        Method method = new Method("selectByExample");
        //方法注解
        method.addAnnotation("@Override");
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType("List"+ "<" + modelName + ">");
        method.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));
        //返回值
        method.setReturnType(methodReturnType);
        String lowCase = firstCharToLowCase(modelName);
        //方法体，逻辑代码
        method.addBodyLine(modelName+"Example "+lowCase+"Example = new "+modelName+"Example();");
        method.addBodyLine(modelName+"Example.Criteria criteria = "+lowCase+"Example.createCriteria();");
        //method.addBodyLine("PageInfo<"+modelName+"> pageInfo =  new PageInfo<>(ticketBatchCancelManager.selectByExample(jsonObject));");
        List<IntrospectedColumn> list = introspectedTable.getBaseColumns();
        String columnType  = "String";
        for (int i = 0; i < list.size(); i++) {
            method.addBodyLine("if (jsonObject.containsKey(\""+list.get(i).getJavaProperty()+"\")) {");
            switch (list.get(i).getJdbcTypeName()){
                case "INTEGER":
                    columnType = "Integer";
                    break;
                case "TIMESTAMP":
                case "DATE":
                    columnType = "Date";
                    break;
                case "LONG":
                case "BIGINT":
                    columnType = "Long";
                    break;
                case "DOUBLE":
                case "DECIMAL":
                    columnType = "Double";
                    break;
                default:
                    columnType = "String";
                    break;
            }
            method.addBodyLine("criteria.and"+captureName(list.get(i).getJavaProperty())+"EqualTo(jsonObject.get"+columnType+"(\""+list.get(i).getJavaProperty()+"\"));}");
        }
        method.addBodyLine("return "+lowCase+"Mapper.selectByExample("+lowCase+"Example);");
        //修饰符
        method.setVisibility(JavaVisibility.PUBLIC);
        clazz.addMethod(method);

        GeneratedJavaFile gjf2 = new GeneratedJavaFile(clazz, targetProject, context.getJavaFormatter());
        return gjf2;
    }


    // 生成controller类
    private GeneratedJavaFile generateController(IntrospectedTable introspectedTable) {

        FullyQualifiedJavaType controller = new FullyQualifiedJavaType(controllerName);
        TopLevelClass clazz = new TopLevelClass(controller);
        //描述类的作用域修饰符
        clazz.setVisibility(JavaVisibility.PUBLIC);

        //引入实体类
        clazz.addImportedType(model);
        //添加顺丰封装在返回注解，并引入相应的类
        //clazz.addImportedType(new FullyQualifiedJavaType("com.sf.erui.common.response.*"));

        clazz.addAnnotation("@RestController");
        //添加@RequestMapping注解，并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("org.springframework.web.bind.annotation.*"));
        clazz.addAnnotation("@RequestMapping(\"/" + firstCharToLowCase(modelName) + "\")");
        //添加@Api注解，并引入相应的类
        clazz.addImportedType(new FullyQualifiedJavaType("io.swagger.annotations.*"));
        String controllerSimpleName = controllerName.substring(controllerName.lastIndexOf(".") + 1);
        clazz.addAnnotation("@Api(tags = \"" + introspectedTable.getRemarks() + "\", value = \"" + controllerSimpleName + "\")");

        //引入controller的父类和model，并添加泛型
//        if (stringHasValue(superController)) {
//            //clazz.addImportedType(superController);
//            clazz.addImportedType(recordType);
//            FullyQualifiedJavaType superInterfac = new FullyQualifiedJavaType(superController + "<" + modelName + ">");
//            //clazz.addSuperInterface(superInterfac);
//        }

        //引入Service
        FullyQualifiedJavaType service = new FullyQualifiedJavaType(serviceName);
        clazz.addImportedType(service);

        //添加Service成员变量
        String serviceFieldName = firstCharToLowCase(serviceName.substring(serviceName.lastIndexOf(".") + 1));
        Field daoField = new Field(serviceFieldName, new FullyQualifiedJavaType(serviceName));

        clazz.addImportedType(new FullyQualifiedJavaType(serviceName));
        clazz.addImportedType(new FullyQualifiedJavaType("javax.annotation.Resource"));
        //描述成员属性 的注解
        daoField.addAnnotation("@Resource");
        //描述成员属性修饰符
        daoField.setVisibility(JavaVisibility.PRIVATE);
        clazz.addField(daoField);

        //描述 方法名
        clazz.addImportedType(new FullyQualifiedJavaType("com.alibaba.fastjson.JSONObject"));

        String methodName = "get";

        Method method = new Method(methodName);
        //方法注解
        //method.addAnnotation("@Override");
        method.addAnnotation("@ApiOperation(value= \"" + "查询" + "\", notes = \"" + "查询" + "\")");
        method.addAnnotation("@PostMapping(value = \"/"+methodName+"\")");
        //String simpleSuperServiceName = superServiceInterface.substring(superServiceInterface.lastIndexOf(".") + 1);
        method.addParameter(new Parameter(new FullyQualifiedJavaType("JSONObject"),"jsonObject"));
        String simpleSuperServiceName = "Response";
        FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType(simpleSuperServiceName + "<" + modelName + ">");
        //返回类型

        method.setReturnType(methodReturnType);

        //方法体，逻辑代码
        method.addBodyLine("return " + "ResponseHelper.buildOk("+serviceFieldName + "."+methodName+"(jsonObject));");
        //修饰符
        method.setVisibility(JavaVisibility.PUBLIC);
        //clazz.addImportedType(superServiceInterface);
        clazz.addMethod(method);

        GeneratedJavaFile gjf2 = new GeneratedJavaFile(clazz, targetProject, context.getJavaFormatter());
        return gjf2;
    }


    private String firstCharToLowCase(String str) {
        char[] chars = new char[1];
        //String str="ABCDE1234";
        chars[0] = str.charAt(0);
        String temp = new String(chars);
        if (chars[0] >= 'A' && chars[0] <= 'Z') {
            return str.replaceFirst(temp, temp.toLowerCase());
        }
        return str;
    }


    private String captureName(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }

}